MacForth Calc
Volume Number: 9
Issue Number: 4
Column Tag: Jörg's Folder
Object Programming in MacForth 
Porting the simple calculator to MacForth
By Jörg Langowski, MacTech Magazine Regular Contributing Author
Note: Source code files accompanying article are located on MacTech CD-ROM orsource code disks.
Those of you who have been with us from the very beginning, V1#1, will
remember that this column started out as a Forth column, and that my first
programming examples were in MacForth. At the time when the Macintosh was
introduced, this was one of the few development systems for the rest of us, and you see,
it has stayed around all the time.
In fact, MacForth is the only Forth system for the Macintosh that is still
commercially supported; you have read about others here, but they are in the public
domain (like Mops and Yerk) or ‘almost’ public domain (like Mach2). Since numerous
calls to the readership for MacForth article submissions were of no avail, I’ve finally
decided to write an example program myself to bring MacForth back into the memory
of the MacTech readers and show how nicely you can write object-oriented code in
MacForth.
So here we’re back to our roots: MacTutor is back to MacTech, and J.L. is back to
MacForth.
Why bother?
Those of you who would never touch Forth with a ten-foot reverse Pole (although
I’m only 5’11”), please don’t turn to the next article immediately. I’ll give you some
basic notions that may help you to understand the example.
A Forth program consists of a sequence of words. Each word is a routine that
works by taking parameters off a stack and eventually leaving results there. Any
number can be considered as just a word that leaves its value on the stack. Forth
development systems normally accept words interactively or from a file and execute
them immediately. All words are separated by blanks. If you type
3
there will be a 3 on top of the stack. The sequence
3 4 +
will put the numbers 3 and 4 on the stack, and then compute their sum, which is
left on the stack. All in all, Forth works like an HP calculator, using reverse Polish
notation.
How about writing programs? There are two “words”, : (colon) and ;
(semicolon), which start and end ‘definitions’. A definition looks like this:
: test 3 4 + ;
This defines the new word test which simply executes the summation of 3 and 4
and leaves the result on the stack. Thus, the first word after the colon is the name of
the newly defined word, and the remaining sequence of words is not executed, but
compiled into a dictionary and by some means or other linked to the name which is kept
in a vocabulary. When the newly defined word is then executed, it will behave as if one
had entered the initial sequence of words in the definition.
Forth programs are typically written by splitting up the program task into very
small units and coding each of them into a word definition, just if one was to write a C
program by coding lots of small routines and assemble them into larger ones. A very
structured approach, which is easy to do with Forth because the low-level definitions
are compiled immediately (by typing them in or loading them from a file), and they
can be tested interactively before using them in the higher-level routines. This
interactive, incremental compiling is one of the great strengths of Forth.
This concludes my short sales pitch for Forth. Before we jump right into the
middle of things with our example, some small Forth words that you’ll come across in
the listing:
! (exclamation mark) takes two values from the stack, an address (top) and a
number (underneath). It stores the number into the 32-bit long word at address. c!
stores the value into the byte at address.
@ (at sign) takes the address from top of stack and gets the 32-bit longword
stored there. c@ gets the 8-bit value at address.
dup duplicates the value on top of the stack, drops it, and exchanges the two top
values.
+, -, *, / are operators that do exactly what you would expect to the two top
values on the stack.
ifthen brackets code that is conditionally executed if the top of the stack is
non-zero.
Other control structures: doloop, caseendcase, beginuntil, beginagain.
Details about their functioning can be found in any good book on Forth. I recommend
“Starting Forth” by Leo Brodie, published by Forth, Inc. But now to the example.
An arithmetic expression parser in Forth
For the example, I decided to convert the calculator program that I presented in
V9#1 from C++ to Forth (I know there are some people who’ll try to kill me for
this). It is not as ridiculous as it seems: The original program was written as an
example for object programming, and you’ll see that the object oriented extensions
contained in MacForth make it relatively easy to port the code. In fact, the only places
where I really had to pay attention to bugs were the routines which input and parsed a
string from the keyboard, and entering and retrieving names from the symbol table.
The whole object-oriented structure of the program worked almost immediately the
way I typed it in.
The calculator works just like the C++ example: it reads in a line with an
arithmetic expression, analyzes it and constructs a ‘syntax tree’ representation from
it, and finally evaluates that syntax tree and prints the result (in order to understand
this month’s example it helps to get out your old V9#1 copy and compare to the C++
code). In the C++ example, we defined classes for the different objects that make up
the syntax tree and constructed an instance of one of those classes for each node of the
tree. Those nodes could be: the operators +,-,*,/ and =, which each connect two other
nodes, the unary minus, and the end nodes, numbers and variables.
To reconstruct the C++ example, let’s first see how object-oriented
programming is achieved in MacForth. MacForth provides so-called acting elements or
actels, which contain their own private data structure and to which messages can be
sent; so these are objects. An actel is defined first through its data structure; MacForth
provides possibilities for structure definitions. You can see this at the beginning of the
listing. As an example, the structure corresponding to a dyadic operator is defined by
structure dyadheader
minElemHeaderSize +
handle: +left
handle: +right
structure.end
This definition does not create the object; it only defines a word dyadheader which
will leave the total size of the structure on the stack when executed, and other words
(+left, +right) that will add a number to the top of stack equal to the offset for the
corresponding element in the structure. So you may define
create my.dyad dyadheader allot
which will allocate space in the dictionary for one copy of this structure, my.dyad, and
then access the two fields of the structure by writing
my.dyad +left
which leaves the address of the ‘left’ field of the structure on the stack. Remark on the
side: the field name in MacForth structure definitions is not local to the structure, but
visible globally. Thus, you cannot define two different structures which have a field
with the same name at different offsets. This is one limitation that one has to live with.
You see that there is some space created at the beginning of the structure (of
length minElemHeaderSize) which contains fields ‘for internal use’. In the current
implementation, this size is 16 bytes. The first longword of these reserved bytes
containes a machine instruction that jumps to the method selector of the class, and the
last longword contains the total size of the header.
The messages that one sends to MacForth actels are again Forth words, which by
convention start with ‘>>’. There are a couple of predefined messages, the ones
interesting to us are >>New (make a new object on the heap), >>Discard (remove the
object from the heap), >>Empty (fill the object’s data space with zeros) and >>Room
(resize the object). New messages are created by writing e.g.
1000 message >>eval
1001 message >>set
as in the example. This assigns the message numbers 1000 and 1001 to the messages
>>eval and >>set. Every message must have a unique number, that’s how they are
distinguished. When a message is sent to an Actel, actually the message number is
passed to a selector, which then calls the appropriate method. The selector, e.g. for the
plus operator, is defined as follows:
1 selector: dyad.msgs
ElemPanic
dyad::New
dyad::Discard
drop
inherited
;selector
1000 selector: plus.msgs
dyad.msgs
plus::Eval
;selector
First, a selector defines the basic methods used in dyadic operators. They are
assigned increasing message numbers, starting with 1. The first word ElemPanic after
the selector name dyad.msgs is the routine which is called if the message number is
outside the range of the selector. Since this is the lowest level of methods, the routine
called here will complain that the message number was not implemented. The next
words correspond to message numbers 1 to 4, or >>New, >>Discard, >>Empty and
>>Room. >>New will call the method dyad::New (the two colons are simpky my